package com.designoutput.ui
{
	import com.adobe.images.PNGEncoder;
	import com.designoutput.elements.BaseElement;
	import com.designoutput.elements.EllipseElement;
	import com.designoutput.elements.ImageElement;
	import com.designoutput.elements.LineElement;
	import com.designoutput.elements.PolyLineElement;
	import com.designoutput.elements.RectElement;
	import com.designoutput.elements.RoundRectElement;
	import com.designoutput.elements.StarElement;
	import com.designoutput.elements.TextElement;
	import com.designoutput.enum.ElementType;
	import com.designoutput.event.CardEvent;
	import com.designoutput.utils.Globals;
	import com.designoutput.utils.Unit;
	import com.designoutput.vo.CardTypeVo;
	import com.designoutput.vo.LayerTypeVo;
	import com.designoutput.vo.TextElementFormatVo;
	
	import flash.display.BitmapData;
	import flash.events.EventDispatcher;
	import flash.geom.Matrix;
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;

	[Event(name="cardDataChanged", type="com.designoutput.event.CardEvent")]
	[Event(name="cardTypeChanged", type="com.designoutput.event.CardEvent")]
	[Event(name="currentLayerChanged", type="com.designoutput.event.CardEvent")]
	[Event(name="unitChanged", type="com.designoutput.event.CardEvent")]
	
	/**
	 * The card, the edit target, will not effect by Orientation
	 */
	public class Card extends EventDispatcher
	{
		public var layers:Vector.<CardFace> = new Vector.<CardFace>();
		private var _layersByName:Object = {};
		
		public var cardType:CardTypeVo;
		
		public var currentLayer:CardFace;
		
		private var _editorsByLayers:Dictionary;

		private var _unit:Unit = Unit.INCHES;
				
		private var _hasPendingChanges:Boolean = false;
		private var _currentCardId:int = 0;
		private var _currentCardName:String = "";

		private var selectedItems:Array = [];
		
		public var shiftKeyActive:Boolean = true;
		
		public function Card():void {
			_editorsByLayers = new Dictionary(true);
			
			changeCardType(0);
			selectedItems = [];
		}
		
		public function getEditorForLayer(layer:CardFace):CardFaceEditor {
			var editor:CardFaceEditor = _editorsByLayers[layer];
			if(editor == null){
				editor = new CardFaceEditor(this);
				editor.setLayer(layer);
				_editorsByLayers[layer] = editor;
			}
			return editor;
		}
		
		/**
		 * Change the card type, will post the index of the card array for it.
		 * @param cardIndex - Number value.
		 */
		public function changeCardType(cardIndex:int):void
		{
			changeUnit(Unit.INCHES);
			
			cardType = Globals.designoutputCard.cardTypeCollection.cardTypes[cardIndex];
			
			layers = new Vector.<CardFace>();
			for each(var layerTypeVo:LayerTypeVo in cardType.layerTypes){
				if(_layersByName[layerTypeVo.name] == null)
					_layersByName[layerTypeVo.name] = new CardFace(this, layerTypeVo);
				
				var layer:CardFace = _layersByName[layerTypeVo.name];
				layer.setLayerTypeVo(layerTypeVo);
				layers.push(layer);
			}
			
			dispatchEvent( new CardEvent( CardEvent.CARD_TYPE_CHANGED ) );
			
			setPendingChanges();
		}
		
		public function get unit():Unit {
			return _unit;
		}
				
		public function changeUnit(newUnit:Unit):void {
			if(_unit != newUnit){
				for each(var cardLayer:CardFace in layers)
					cardLayer.changeUnit(newUnit);
				
				_unit = newUnit;
				
				dispatchEvent( new CardEvent( CardEvent.UNIT_CHANGED ) );
				
				//TODO: Decouple card from left panel
				updateElementsOption();
				
				setPendingChanges();
			}			
		}
		
		public function changeLayerOrientation(layer:CardFace, orientation:String):void {
			if(layer.orientation != orientation){
				changeLayerSize(layer, layer.heightInUnit, layer.widthInUnit);
			}
		}
		
		public function changeLayerSize(layer:CardFace, w:Number, h:Number):void {
			if(!layer.sizeName) {
				layer.changeSize(w, h);
			} else {
				for each(var l:CardFace in layers){
					if(l.sizeName == layer.sizeName)
						l.changeSize(w, h);
				}
			}
						
			setPendingChanges();
		}
			
		public function changeCurrentLayer(layer:CardFace):void
		{
			currentLayer = layer;
			dispatchEvent(new CardEvent(CardEvent.CURRENT_LAYER_CHANGED));
		}
		
		
		
		public function get hasPendingChanges():Boolean {
			return _hasPendingChanges;
		}
		
		public function setPendingChanges():void {
			_hasPendingChanges = true;
		}
		
		public function resetPendingChanges():void {
			_hasPendingChanges = false;
		}
		
		
		
		public function get cardId():int {
			return _currentCardId;
		}
		public function get cardName():String {
			return _currentCardName;
		}
		
		public function setCardData(cardId:int, cardName:String):void {
			_currentCardId = cardId;
			_currentCardName = cardName;
			dispatchEvent( new CardEvent( CardEvent.CARD_DATA_CHANGED ) );
		}
		
		
		
		public function toXML():XML
		{
			var cardXML:XML = <CardPage />;
						
			cardXML.@type = cardType.name;
			
			cardXML.@unit = _unit.name;

			for each(var layer:CardFace in layers){
				var layerNode:XML = <layer />;
				layerNode.@name = layer.name;
				cardXML.appendChild(layerNode.appendChild(layer.toXML()));
			}
			
			return cardXML;
		}
		
		public function fromXML(cardXML:XML):void
		{			
			if("@type" in cardXML){
				for(var i:int = 0; i< Globals.designoutputCard.cardTypeCollection.cardTypes.length; i++){
					if(Globals.designoutputCard.cardTypeCollection.cardTypes[i].name == String(cardXML.@type)){
						changeCardType(Number(i));
						break;
					}
				}
			}
			
			if("@unit" in cardXML){
				changeUnit(Unit.getUnitByName(String(cardXML.@unit)));
			}

			for each(var layerNode:XML in cardXML.layer){
				var layerName:String = String(layerNode.@name);
				var layer:CardFace = _layersByName[layerName];
				if(layer)
					layer.fromXML(layerNode.elements()[0]);
			}
			
			resetPendingChanges();
		}
		
		public function getPNGThumb():ByteArray {			
			var height:Number = 200;
			var totalWidth:Number = 0;
			
			for each(var layer:CardFace in layers){
				if(layer.print){
					totalWidth += (height / layer.height) * layer.width;
				}
			}
			
			var bitmapData:BitmapData = new BitmapData(totalWidth, height);
			var currX:Number = 0;
			
			for each(var cardLayer:CardFace in layers){
				if(cardLayer.print){
					var scale:Number = height / cardLayer.height;
					
					var transformMatrix:Matrix = new Matrix();
					transformMatrix.scale(scale, scale);
					transformMatrix.translate(currX, 0);
					currX += scale * cardLayer.width;
					
					bitmapData.draw(cardLayer, transformMatrix, null, null, null, true);
				}
			}
			
			return PNGEncoder.encode(bitmapData);
		}
			
		
		
		
		
		
		
		/**
		 * To call the ancestor to switch layout or gallery, to chose image.  
		 * @param element
		 * @param bSwitch
		 * 
		 */		
		public function chooseImage(element:ImageElement, bSwitch:Boolean):void
		{
			Globals.designoutputCard.chooseImage(element,bSwitch);
		}
		
		public function setBackgroundImage(imageUrl:String):void{
			currentLayer.setBackgroundImage(imageUrl);
			
			setPendingChanges();
		}
		
		/****************************************************************************************************/
		
		public function dropSelItem(item:*):void
		{
			var arr:Array = this.getSelectItems();
			for (var i:Number=arr.length - 1; i >= 0; i--)
			{
				if (!item || arr[i] == item)
				{
					(arr[i] as BaseElement).selected = false;
					arr.splice(i, 1);
					if (item)
						break;
				}
			}
			//No elements selected, when chose image from gallery, it will update background image for card, not for ImageElement
			this.chooseImage(null, false);
		}
		
		public function dropSelItems():void
		{
			var arr:Array = getSelectItems();
			for (var i:Number=0; i < arr.length; i++)
			{
				(arr[i] as BaseElement).selected = false;
			}
			this.chooseImage(null, false);
			arr.splice(0);
		}
		
		public function addSelItem(selItem:BaseElement):void
		{
			var arr:Array = this.getSelectItems();
			if (selItem)
			{
				var bExist:Boolean=false;
				for (var i:Number=0; i < arr.length; i++)
				{
					if (arr[i] == selItem)
					{
						bExist=true;
						break;
					}
				}
				if (!bExist)
				{
					arr.push(selItem);
					if(selItem.getClassName() == ElementType.ImageElement){
						//If the current element is ImageElement, it could change image by chose image from gallery.
						//If the image element is not single, it will change image for one of them
						this.chooseImage(ImageElement(selItem), false);
					}
				}
				selItem.selected = true;
			}
			
		}
		
		public function setSelectItemRotation(rotation:Number):void
		{
			var arr:Array = this.getSelectItems();
			if (arr.length > 1)
				return;
			arr[0].rotate(rotation);
			
			setPendingChanges();
		}
		
		/**
		 * For common card there will be only Front&Back layer, so the current select items array is the currentSelectItemsArr.
		 * <BR>But for T-Shirts page, there will be 2 current layers to uesd. So just assign 2 items array.
		 * @return 
		 * 
		 */		
		public function getSelectItems():Array
		{
			return this.selectedItems;
		}
		
		public function setSelSize(nW:Number, nH:Number):void
		{	
			var arr:Array = this.getSelectItems();
			if (arr.length > 1)
				return;
			for (var i:int=0; i < arr.length; i++)
			{
				arr[i].setSize(nW, nH);
				arr[i].update();
				arr[i].drawElement();
			}
		}
		
		
		/******************************************************************/
		
		
		
		/**
		 * Update element information, information will be show on the LeftToolBar when chose associated element type
		 */
		public function updateElementsOption():void
		{
			var arr:Array = this.getSelectItems();
			var selectElementsNum:int=arr.length;
			if (selectElementsNum > 0)
			{
				if (selectElementsNum > 1)
				{
					var element:BaseElement=BaseElement(arr[0]);
					Globals.designoutputCard.command.optionSetValues(ElementType.BaseElement, element);
				}
				else
				{
					var elementType:String=arr[0].getClassName();
					switch (elementType)
					{
						case ElementType.RectangleElement:
							var rectangleElement:RectElement = RectElement(arr[0]);
							Globals.designoutputCard.command.optionSetValues(ElementType.RectangleElement, rectangleElement);
							break;
						case ElementType.RoundRectangleElement:
							var roundRectElement:RoundRectElement = RoundRectElement(arr[0]);
							Globals.designoutputCard.command.optionSetValues(ElementType.RoundRectangleElement, roundRectElement);
							break;
						case ElementType.EllipseElement:
							var ellipseElement:EllipseElement = EllipseElement(arr[0]);
							Globals.designoutputCard.command.optionSetValues(ElementType.EllipseElement, ellipseElement);
							break;
						case ElementType.StarElement:
							var starElement:StarElement = StarElement(arr[0]);
							Globals.designoutputCard.command.optionSetValues(ElementType.StarElement, starElement);
							break;
						case ElementType.PolyLineElement:
							var polyLineElement:PolyLineElement = PolyLineElement(arr[0]);
							Globals.designoutputCard.command.optionSetValues(ElementType.PolyLineElement, polyLineElement);
							break;
						case ElementType.LineElement:
							var lineElement:LineElement = LineElement(arr[0]);
							Globals.designoutputCard.command.optionSetValues(ElementType.LineElement, lineElement);
							break;
						case ElementType.TextElement:
							var textElement:TextElement=TextElement(arr[0]);
							Globals.designoutputCard.command.optionSetValues(ElementType.TextElement, textElement);
							break;
						case ElementType.ImageElement:
							var imageElement:ImageElement=ImageElement(arr[0]);
							Globals.designoutputCard.command.optionSetValues(ElementType.ImageElement, imageElement);
							break;
						default:
							break;
					}
				}
			}
			else
			{
				Globals.designoutputCard.command.disableOptions();
			}
		}
		
		/**
		 * Change the element's edge line size
		 * @param lineColor
		 *
		 */
		public function changeElementLineSize(lineSize:Number=1):void
		{	
			var arr:Array = this.getSelectItems();
			var selectElementsNum:int=arr.length;
			if (selectElementsNum > 0)
			{
				var element:BaseElement=BaseElement(arr[0]);
				element.lineSize=lineSize;
				element.validate();
			}
		}
		
		/**
		 * Change the element's edge line color
		 * @param lineColor
		 *
		 */
		public function changeElementLineColor(lineColor:uint=0):void
		{
			var arr:Array = this.getSelectItems();
			var selectElementsNum:int=arr.length;
			if (selectElementsNum > 0)
			{
				var element:BaseElement=BaseElement(arr[0]);
				element.lineColor=lineColor;
				element.validate();
			}
		}
		
		/**
		 *  Change the element's edge line alpha
		 * @param lineAlpha
		 *
		 */
		public function changeElementLineAlpha(lineAlpha:Number=1):void
		{
			var arr:Array = this.getSelectItems();
			var selectElementsNum:int=arr.length;
			if (selectElementsNum > 0)
			{
				var element:BaseElement=BaseElement(arr[0]);
				element.lineAlpha=lineAlpha;
				element.validate();
			}
		}
		
		/**
		 * Change the element's background color
		 * @param fillColor
		 *
		 */
		public function changeElementFillColor(fillColor:uint=0):void
		{
			var arr:Array = this.getSelectItems();
			var selectElementsNum:int=arr.length;
			if (selectElementsNum > 0)
			{
				var element:BaseElement=BaseElement(arr[0]);
				element.fillColor=fillColor;
				element.validate();
			}
		}
		
		/**
		 *  Change the element's background alpha
		 * @param lineAlpha
		 *
		 */
		public function changeElementFillAlpha(fillAlpha:Number=1):void
		{
			var arr:Array = this.getSelectItems();
			var selectElementsNum:int=arr.length;
			if (selectElementsNum > 0)
			{
				var element:BaseElement=BaseElement(arr[0]);
				element.fillAlpha=fillAlpha;
				element.validate();
			}
		}
		
		/**
		 * Set if the element use the edge line or not,for commom element
		 * @param useFill
		 *
		 */
		public function changeElementUseLine(useLine:Boolean=true):void
		{
			var arr:Array = this.getSelectItems();
			var selectElementsNum:int=arr.length;
			if (selectElementsNum > 0)
			{
				var element:BaseElement=BaseElement(arr[0]);
				element.useLine = useLine;
				element.validate();
			}
		}
		
		/**
		 * Set if the element fill background color or not,for commom element
		 * @param useFill
		 *
		 */
		public function changeElementUseFill(useFill:Boolean=true):void
		{
			var arr:Array = this.getSelectItems();
			var selectElementsNum:int=arr.length;
			if (selectElementsNum > 0)
			{
				var element:BaseElement=BaseElement(arr[0]);
				element.useFill = useFill;
				element.validate();
			}
		}
		
		/**
		 * Chaneg the Radius number,  only  for RoundRectElement
		 * @param radius
		 *
		 */
		public function changeElementRadius(radius:Number):void
		{
			var arr:Array = this.getSelectItems();
			var selectElementsNum:int=arr.length;
			if (selectElementsNum > 0)
			{
				var element:RoundRectElement=RoundRectElement(arr[0]);
				element.setR(radius);
				element.validate();
			}
		}
		
		/**
		 * Change the Star Points number, only for Star/Poly Element
		 * @param pointsNum
		 *
		 */
		public function changeElementStarPoints(pointsNum:Number=3):void
		{
			var arr:Array = this.getSelectItems();
			var selectElementsNum:int=arr.length;
			if (selectElementsNum > 0)
			{
				var element:StarElement=StarElement(arr[0]);
				element.setStarPoints(pointsNum);
				element.validate();
			}
		}
		
		/**
		 * Change the Groin Ratio,  only for Star/Poly Element
		 * @param ratioNum
		 *
		 */
		public function changeElementGroinRatio(ratioNum:Number=3):void
		{
			var arr:Array = this.getSelectItems();
			var selectElementsNum:int=arr.length;
			if (selectElementsNum > 0)
			{
				var element:StarElement=StarElement(arr[0]);
				element.setStarCenterDist(ratioNum);
				element.validate();
			}
		}
		
		/**
		 * Change the text format for TextElement
		 * @param obj - new text format
		 *
		 */
		public function changeTextFormat(obj:TextElementFormatVo):void
		{
			var selectedItems:Array = getSelectItems();
			var selectElementsNum:int=selectedItems.length;
			if (selectElementsNum > 0)
			{
				var element:TextElement = selectedItems[0] as TextElement;
				if (element)
				{
					element.bold = obj.bold;
					element.italic = obj.italic;
					element.underline = obj.underline;
					element.size = obj.size;
					element.font = obj.font;
					element.kerning = obj.kerning;
					element.leading = obj.leading;
					element.letterSpacing = obj.letterSpacing;
					element.shadow = obj.shadow;
					element.align = obj.align;
					element.vAlign = obj.vAlign;
					
					element.fillColor = obj.fillColor;
					element.fillAlpha = obj.fillAlpha;
				}
			}
		}
		
		/**
		 * Change the image element proportion as the original one.
		 */		
		public function changeImageProportion():void{
			var arr:Array = this.getSelectItems();
			var selectElementsNum:int=arr.length;
			if (selectElementsNum > 0)
			{
				var element:ImageElement=ImageElement(arr[0]);
				if (element)
				{
					if(element.getOriginalProportion() > 0){
						if(element.height / element.width < element.getOriginalProportion()) 
						{
							element.width = element.height / element.getOriginalProportion();
						}else {
							element.height = element.width * element.getOriginalProportion();
						}
						element.validate();
					}
				}
			}
		}
		
		/**
		 * Change the element detph
		 * @param layoutType
		 *
		 */
		public function changeElementDepth(layoutType:String):void
		{
			currentLayer.changeElementDepth(layoutType, selectedItems);
		}
		
		public function getSelectedItemsXML():XML {
			var selected:Array = this.getSelectItems();
			
			var resultArray:XML = <elements/>;
			for each(var element:BaseElement in selected){
				resultArray.appendChild(element.toXML());
			}
			
			return resultArray;
		}
		
		/**
		 * Delete selected element(s),it happen when click the trash button on the Left Tool Bar
		 * OR when delete key is pre.
		 */
		public function deleteSelectElements():void
		{
			var arr:Array;
			var layer:CardFace;
			
			arr = this.selectedItems;
			layer = this.currentLayer;
			
			var eleNum:Number=arr.length;
			if (eleNum > 0)
			{
				for (var i:int=0; i < eleNum; i++)
				{
					var baseElement:BaseElement=BaseElement(arr[i]);
					layer.removeElement(baseElement);
				}
				this.dropSelItems();
				updateElementsOption();
			}			
		}
	}
}